package net.southbird.cas;

import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.CleanUpTimerTask;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.AbstractTicketValidationFilter;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.ProxyList;
import org.jasig.cas.client.validation.ProxyListEditor;
import org.jasig.cas.client.validation.TicketValidator;

/**
 * ticket校验
 * 
 * @author lfzxs@qq.com
 * @CreateDate May 7, 2010
 */
public class AutoCasProxyReceivingTicketValidationFilter extends
		AbstractTicketValidationFilter {
	
	protected final Log log = LogFactory.getLog(getClass());

	private static final String[] RESERVED_INIT_PARAMS = new String[] {
			"proxyReceptorUrl", "acceptAnyProxy", "allowedProxyChains",
			"casServerUrlPrefix", "proxyCallbackUrl", "renew",
			"exceptionOnValidationFailure", "redirectAfterValidation",
			"useSession", "serverName", "service", "artifactParameterName",
			"serviceParameterName", "encodeServiceUrl", "millisBetweenCleanUps" };

	private static final int DEFAULT_MILLIS_BETWEEN_CLEANUPS = 60 * 1000;

	/**
	 * The URL to send to the CAS server as the URL that will process proxying
	 * requests on the CAS client.
	 */
	private String proxyReceptorUrl;

	private Timer timer;

	private TimerTask timerTask;

	private int millisBetweenCleanUps;
	

	private String acceptAnyProxy = null;
	
	private String allowedProxyChains = null ;
	
	private String casServerUrlPrefix  = null;
	
	private String proxyCallbackUrl = null ;
	
	private String renew = "false" ;
	
	private String encoding = "utf-8" ;
	
	
	

	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	/**
	 * Storage location of ProxyGrantingTickets and Proxy Ticket IOUs.
	 */
	private ProxyGrantingTicketStorage proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();

	protected void initInternal(final FilterConfig filterConfig)
			throws ServletException {
		
		super.initInternal(filterConfig);
		setProxyReceptorUrl(getPropertyFromInitParams(filterConfig,
				"proxyReceptorUrl", null));

		final String proxyGrantingTicketStorageClass = getPropertyFromInitParams(
				filterConfig, "proxyGrantingTicketStorageClass", null);

		if (proxyGrantingTicketStorageClass != null) {
			try {
				final Class storageClass = Class
						.forName(proxyGrantingTicketStorageClass);
				this.proxyGrantingTicketStorage = (ProxyGrantingTicketStorage) storageClass
						.newInstance();
			} catch (final Exception e) {
				throw new RuntimeException(e);
			}
		}

		log.trace("Setting proxyReceptorUrl parameter: "
				+ this.proxyReceptorUrl);
		this.millisBetweenCleanUps = Integer
				.parseInt(getPropertyFromInitParams(filterConfig,
						"millisBetweenCleanUps", Integer
								.toString(DEFAULT_MILLIS_BETWEEN_CLEANUPS)));
		
		setTicketValidator(getTicketValidator(filterConfig)) ;
	}

	public void init() {
		super.init();
		CommonUtils.assertNotNull(this.proxyGrantingTicketStorage,
				"proxyGrantingTicketStorage cannot be null.");

		if (this.timer == null) {
			this.timer = new Timer(true);
		}

		if (this.timerTask == null) {
			this.timerTask = new CleanUpTimerTask(
					this.proxyGrantingTicketStorage);
		}
		this.timer.schedule(this.timerTask, this.millisBetweenCleanUps,
				this.millisBetweenCleanUps);
	}

	
	public String getAcceptAnyProxy() {
		return acceptAnyProxy;
	}

	public void setAcceptAnyProxy(String acceptAnyProxy) {
		this.acceptAnyProxy = acceptAnyProxy;
	}

	public String getAllowedProxyChains() {
		return allowedProxyChains;
	}

	public void setAllowedProxyChains(String allowedProxyChains) {
		this.allowedProxyChains = allowedProxyChains;
	}

	public String getCasServerUrlPrefix() {
		return casServerUrlPrefix;
	}

	public void setCasServerUrlPrefix(String casServerUrlPrefix) {
		this.casServerUrlPrefix = casServerUrlPrefix;
	}

	public String getProxyCallbackUrl() {
		return proxyCallbackUrl;
	}

	public void setProxyCallbackUrl(String proxyCallbackUrl) {
		this.proxyCallbackUrl = proxyCallbackUrl;
	}

	public String getRenew() {
		return renew;
	}

	public void setRenew(String renew) {
		this.renew = renew;
	}

	/**
	 * Constructs a Cas20ServiceTicketValidator or a Cas20ProxyTicketValidator
	 * based on supplied parameters.
	 * 
	 * @param filterConfig
	 *            the Filter Configuration object.
	 * @return a fully constructed TicketValidator.
	 */
	@Override
	protected TicketValidator getTicketValidator(
			final FilterConfig filterConfig) {
		final String allowAnyProxy = getPropertyFromInitParams(filterConfig,
				"acceptAnyProxy", this.acceptAnyProxy);
		final String allowedProxyChains = getPropertyFromInitParams(
				filterConfig, "allowedProxyChains", this.allowedProxyChains);
		final String casServerUrlPrefix = getPropertyFromInitParams(
				filterConfig, "casServerUrlPrefix", this.casServerUrlPrefix);
		final Cas20ServiceTicketValidator validator;
		

		if (CommonUtils.isNotBlank(allowAnyProxy)
				|| CommonUtils.isNotBlank(allowedProxyChains)) {
			final Cas20ProxyTicketValidator v = new Cas20ProxyTicketValidator(
					casServerUrlPrefix);
			v.setAcceptAnyProxy(parseBoolean(allowAnyProxy));
			v.setAllowedProxyChains(createProxyList(allowedProxyChains));
			validator = v;
					
		} else {
			validator = new Cas20ServiceTicketValidator(casServerUrlPrefix);
		}
		validator.setEncoding(this.encoding);
		validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig,
				"proxyCallbackUrl", this.proxyCallbackUrl));
		validator.setProxyGrantingTicketStorage(proxyGrantingTicketStorage);
		validator
				.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix,this.encoding));
		validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig,
				"renew", this.renew)));

		final Map additionalParameters = new HashMap();
		final List params = Arrays.asList(RESERVED_INIT_PARAMS);

		for (final Enumeration e = filterConfig.getInitParameterNames(); e
				.hasMoreElements();) {
			final String s = (String) e.nextElement();

			if (!params.contains(s)) {
				additionalParameters.put(s, filterConfig.getInitParameter(s));
			}
		}

		validator.setCustomParameters(additionalParameters);

		return validator;
	}

	protected ProxyList createProxyList(final String proxies) {
		if (CommonUtils.isBlank(proxies)) {
			return new ProxyList();
		}

		final ProxyListEditor editor = new ProxyListEditor();
		editor.setAsText(proxies);
		return (ProxyList) editor.getValue();
	}

	public void destroy() {
		super.destroy();
		this.timer.cancel();
	}

	/**
	 * This processes the ProxyReceptor request before the ticket validation
	 * code executes.
	 */
	protected boolean preFilter(final ServletRequest servletRequest,
			final ServletResponse servletResponse, final FilterChain filterChain)
			throws IOException, ServletException {
		final HttpServletRequest request = (HttpServletRequest) servletRequest;
		final HttpServletResponse response = (HttpServletResponse) servletResponse;
		final String requestUri = request.getRequestURI();
		
		

		log.trace("preFilter:"+request.getQueryString()) ;
		String serverName = WebUtil.getHost(request.getRequestURL().toString());//String.format("http://%s:%s", request.getLocalAddr(),request.getLocalPort()) ;
		setServerName(serverName) ;
		
		log.trace("preFilter:"+serverName) ;
		
		
		
		if (CommonUtils.isEmpty(this.proxyReceptorUrl)
				|| !requestUri.endsWith(this.proxyReceptorUrl)) {
			return true;
		}

		CommonUtils.readAndRespondToProxyReceptorRequest(request, response,
				proxyGrantingTicketStorage);

		return false;
	}

	public void setProxyReceptorUrl(final String proxyReceptorUrl) {
		this.proxyReceptorUrl = proxyReceptorUrl;
	}

	public void setProxyGrantingTicketStorage(
			final ProxyGrantingTicketStorage storage) {
		proxyGrantingTicketStorage = storage;
	}

	public void setTimer(final Timer timer) {
		this.timer = timer;
	}

	public void setTimerTask(final TimerTask timerTask) {
		this.timerTask = timerTask;
	}

	public void setMillisBetweenCleanUps(final int millisBetweenCleanUps) {
		this.millisBetweenCleanUps = millisBetweenCleanUps;
	}
}
